﻿using CommonEngine;
using CommonFP;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace CommonDevelop.DB
{
    public abstract class BizService<T, TDB> : IBaseService<T> where T : BaseEn, new() where TDB : IBase<T>
    {
        protected readonly TDB repository_DB;
        protected readonly IHttpContextAccessor http_ContextAccessor;
        /// <summary>
        /// 基类构造注入，数据层对应的业务切面
        /// </summary>
        public BizService(TDB repository)
        {
            this.repository_DB = repository;
            this.http_ContextAccessor = IocContainer.Resolve<IHttpContextAccessor>();
        }


        public virtual string IP()
            => FP.Try(() => this.http_ContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString())
            .Run().ToValidation().Outcome();

        public virtual string BuildSeq()
            => string.Empty;

        public virtual string BusinessKey()
            => "Business2020";

        public virtual bool CleanUp()
            => false;


        public virtual IEnumerable<T> GetList(string keyword = null, bool IsDelete = false)
            => FP.Try(() => this.repository_DB.GetList(keyword, IsDelete))
            .Run()
            .Match(e => Enumerable.Empty<T>(), a => a);

        public virtual IEnumerable<T> GetPageList(Pagination Pag, string keyword = null, bool IsDelete = false)
            => FP.Try(() => this.repository_DB.GetPageList(Pag, keyword, IsDelete))
            .Run()
            .Match(e => Enumerable.Empty<T>(), a => a);

        public virtual IEnumerable<TVO> GetList<TVO>(string keyword = null, bool IsDelete = false)
            where TVO : class, new()
            => this.GetList(keyword, IsDelete).MapToList<T, TVO>();

        public virtual IEnumerable<TVO> GetPageList<TVO>(Pagination Pag, string keyword = null, bool IsDelete = false)
            where TVO : class, new()
            => this.GetPageList(Pag, keyword, IsDelete).MapToList<T, TVO>();


        public virtual async Task<T> GetEntity(object keyValue)
            => await FP.Async(this.repository_DB.GetEntity(keyValue));

        public virtual async Task<T> GetEntity(Expression<Func<T, bool>> expression, bool IsDelete)
            => await FP.Async(this.repository_DB.GetEntity(expression, IsDelete));


        public virtual Validation<bool> Add_En(T En)
            => FP.Try(() =>
            {
                En.Completion(this.BusinessKey() ?? "");
                return this.repository_DB.Add_Entity(En, true);
            }).Run().ToValidation();

        public virtual Validation<bool> Add_En(IEnumerable<T> EnLs)
            => FP.Try(() =>
            {
                EnLs.ForEach(a => a.Completion(this.BusinessKey() ?? ""));
                return this.repository_DB.Add_Entities(EnLs, true) > 0;
            }).Run().ToValidation();


        public virtual Validation<bool> Save_En(T En)
            => FP.Try(() =>
            {
                En.Completion(this.BusinessKey() ?? "");
                return this.repository_DB.Update_Entity(En, true);
            }).Run().ToValidation();

        public virtual Validation<bool> Save_En(IEnumerable<T> EnLs)
            => FP.Try(() =>
            {
                EnLs.ForEach(a => a.Completion(this.BusinessKey() ?? ""));
                return this.repository_DB.Update_Entities(EnLs, true) > 0;
            }).Run().ToValidation();


        public virtual Validation<bool> VirtualDelete(object keyValue, string UserId, string UserName)
            => FP.Invalid("Not implemented");

        public virtual Validation<bool> Delete(object keyValue)
            => FP.Try(() => keyValue
            .Pipe(obj => SugarBizDB.Set_PrimaryKeyValue<T>(obj))
            .Pipe(this.repository_DB.Delete_Entity))
            .Run().ToValidation();


        public virtual Option<TVO> Get<TVO>(object keyValue)
            where TVO : class, new()
        {
            var v = from en in this.GetEntity(keyValue)
                    select en.MapTo<TVO>();

            return v.OrElse(() => FP.Async(default(TVO))).Map(a => a.ToOption()).Result;
        }

        public virtual Option<TVO> GetMap<TVO>(T en)
            where TVO : class, new()
            => en.ToOption().Map(a => a.MapTo<TVO>());

        public virtual IEnumerable<TVO> GetMapList<TVO>(IEnumerable<T> EnList)
           where TVO : class, new()
           => EnList.MapToList<T, TVO>();

        public virtual IEnumerable<TVO> GetBind<TVO>(T en)
            where TVO : class, new()
            => FP.Try(() => this.repository_DB.GetPull<TVO>(en))
            .Run().Match(e => Enumerable.Empty<TVO>(), a => a);


        public virtual Validation<bool> Add<TVO>(TVO En)
            where TVO : class, new()
            => En.ToOption()
                .Map(a => a.MapTo<T>())
                .Map(a => this.Add_En(a))
                .Outcome();

        /// <summary>
        /// 保存视图（实体结构一致）（非FP）
        /// </summary>
        public virtual Validation<bool> Save<TVO>(TVO En)
            where TVO : class, new()
        {
            TVO result = En as TVO;
            var pipe1 = FP.MakePipe<TVO, T>(a => FP.Async(a.MapTo<T>()));
            var pipe3 = FP.MakePipe<TVO, T>(a => this.GetEntity(SugarBizDB.Get_DtoKeyValue<T>(a)));

            var v = from Dto in FP.Async(result.IsValidData())
                    from uB in pipe1(Dto)
                    from olduB in pipe3(Dto)
                    let NewuB = olduB.MergeEn(uB)
                    let _ = FP.ToFunc(() => NewuB.Completion(this.BusinessKey() ?? ""))
                    let b = this.repository_DB.Update_Entity(NewuB, true)
                    select b;

            var t = v.Recover(ex => FP.Error(ex.Message));
            t.Wait();

            return t.Result;
        }
    }
}
